#include <iostream>
#include <string>
#include <map>

#include <boost/lexical_cast.hpp>

#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
using namespace boost::filesystem;
using namespace boost;

struct Message
{
    unsigned int id;
    std::string args;

    Message( unsigned int _id, std::string _args)
    {
        id = _id;
        args = _args;
    }
};

std::map<std::string, Message*> messageList;
unsigned int author = 0;
unsigned int max = 0;

std::time_t messageHeaderTime;

const regex declRegex(".*DECL_MSG\\(.*?, *(.*?) *,.*?, *(.*?) *\\)");

void parseSource(std::string line)
{
    smatch expr;

    if( regex_match(line, expr, declRegex))
    {
        Message* & msg = messageList[expr[1]];

        if( !msg)
        {
            std::cout << "Adding " << expr[1] << std::endl;
            msg = new Message( ++max | (author << 10), expr[2]);
        }
        else if( msg->args != expr[2])
        {
            std::cout << "Updating " << expr[1] << std::endl;
            msg->args = expr[2];
        }
    }
}

const regex defRegex(".*DEF_MSG\\( *([0-9]*?) *, *(.*?) *, *(.*?) *\\)");

void parseMessages(std::string line)
{
    smatch expr;

    if( regex_match(line, expr, defRegex))
    {
        unsigned int id = lexical_cast<unsigned int>(expr[1]);
        messageList[expr[2]] = new Message(id, expr[3]);

        if( ((id >> 10) == author) && ((id & 0x3ff) > max))
            max = id & 0x3ff;
    }
}

void parseFile( path p, void (*parse)(std::string line))
{
    if( last_write_time(p) <= messageHeaderTime)
        return;

    std::cout << "Parsing file " << p.filename() << std::endl;

    ifstream file(p);

    std::string line;

    while( file)
    {
        char c = file.get();

        if( c == '\n')
        {
            if( line.length())
                parse(line);
            line = "";
            continue;
        }
        else if( c != '\t')
            line += c;
    }

    if( line.length())
        parse(line);

    file.close();
}

void loop(path p)
{
    if( !exists(p))
        return;
    if( is_directory(p))
    {
        directory_iterator end_iter;
        for ( directory_iterator iter( p ); iter != end_iter; iter++ )
        {
            loop( iter->path());
        }
    }
    else
    {
        if( p.extension() == ".cpp")
            parseFile( p, parseSource);
    }
}

const std::string usage("Usage : msgparse author_id [definition file] (files|directories)*");

int main(int argc, const char** argv )
{
    path p;
    path f;

    if( argc < 2)
    {
        std::cout << usage << std::endl;
        return 0;
    }


    std::string arg1(argv[1]);

    if( (arg1 == "--help") || (arg1 == "-h"))
    {
        std::cout << "Usage : msgparse author_id [-d «definition file»] (files|directories)*\n"
                  << "author_id is the id of the author used to register new messages.\n"
                  << "«definition file» is the C++ header where messages are defined.\n"
                  << "files are the file to parse for new/updated messages.\n"
                  << "directories are directories to search for file to parse." << std::endl;
        return 0;
    }

    try
    {
        author = lexical_cast<unsigned int>(arg1);
    }
    catch(bad_lexical_cast)
    {
        std::cerr << usage << "\nauthor should be an unsigned number less than 64." << std::endl;
        return 1;
    }

    if( author > 64)
    {
        std::cerr << usage << "\nauthor should be an unsigned number less than 64." << std::endl;
        return 1;
    }

    unsigned int pos = 2;

    if( argc > 2 && std::string(argv[2]) == "-d")
    {
        if( argc < 4)
        {
            std::cerr << usage << "\noption -d expects a definition file, none is provided." << std::endl;
            return 1;
        }
        f = path(argv[3]);
        pos++;
    }
    else
        f = path("./messages_def.h");

    parseFile(f, parseMessages);
    messageHeaderTime = last_write_time(f);

    if( pos == (unsigned int)argc) //Try default source path
    {
        p = path( "./src");
        loop(p);
    }

    for( unsigned int i=pos; i < (unsigned int)argc; i++)
    {
        p = path(argv[i]);
        loop(p);
    }


    ofstream output(f);

    for( std::map<std::string, Message*>::iterator iter = messageList.begin(); iter != messageList.end(); iter++)
    {
        output << "DEF_MSG(" << iter->second->id << ", " << iter->first << ", " << iter->second->args << ")\n";
    }

    output.close();
    return 0;
}
